#include "../userprog/syscall.h"

/*/======== Ticket Taker Function ========*/
void main()
{

	int myIndex;

	/*Shared Data*/
	int latestIndexLock;
	int latestIndex;
	int latestIndexCondition;
	int ttStateLock;
	int ttState;
	int NUM_TICKET_TAKERS;
	int CLERK_AVAILABLE;
	int CLERK_ON_BREAK;
	int CLERK_BUSY;
	int ttInteractionCondition;
	int ttInteractionLock;
	int ttBreakLock;
	int ttBreakCondition;
	int ttBreakStopLock;
	int ttBreakStopCondition;
	int ttLineManager;
	int ttLineLength;
	int ticketTakerFull;
	int beginTakingTicketsLock;
	int beginTakingTicketsCondition;
	int ttForcedToStop;
	int ticketCountBoxLock;
	int ticketCountBox;
	int numTicketsGiven;
	int SCREENING_ROOM_CAPACITY;
	int ticketTakerSaidToProceed;
	int ticketTakerFullLock;
	int ttLobbyLock;
	int ttStopLock;
	int ttStopCondition;
	int ttLineCondition;
	int PRINT_DEBUG_MESSAGES;
	int PRINT_OUTPUT_MESSAGES;
	
	/*Acquire Shared Data*/
NUM_TICKET_TAKERS = CreateMV(1,"NUM_TICKET_TAKERS",21);	
latestIndexLock = CreateLock("latestIndexLock", 15);
latestIndex = CreateMV(1,"latestIndex", 11);
latestIndexCondition = CreateCondition("latestIndexCondition",20);
ttStateLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttStateLock",11);
ttState = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttState",7);
CLERK_AVAILABLE = CreateMV(1,"CLERK_AVAILABLE",15);
CLERK_ON_BREAK = CreateMV(1,"CLERK_ON_BREAK",14);
CLERK_BUSY = CreateMV(1,"CLERK_BUSY",10);
ttInteractionCondition = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttInteractionCondition",22);
ttInteractionLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttInteractionLock",17);
ttBreakLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttBreakLock",11);
ttBreakCondition = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttBreakCondition",16);
ttBreakStopLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttBreakStopLock",15);
ttBreakStopCondition = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttBreakStopCondition",20);
ttLineManager = CreateLock("ttLineManager",13);
ttLineLength = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttLineLength",12);
ttLineCondition = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttLineCondition",15);
ticketTakerFull = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ticketTakerFull",15);
beginTakingTicketsLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0), "beginTakingTicketsLock",22);
beginTakingTicketsCondition = CreateMV(GetMV(NUM_TICKET_TAKERS,0), "beginTakingTicketsCondition",27);
ttForcedToStop = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ttForcedToStop",14);
ticketCountBoxLock = CreateLock("ticketCountBoxLock",18);
ticketCountBox = CreateMV(1,"ticketCountBox",14);	
numTicketsGiven = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"numTicketsGiven",15);
SCREENING_ROOM_CAPACITY = CreateMV(1,"SCREENING_ROOM_CAPACITY",23);
ticketTakerSaidToProceed = CreateMV(GetMV(NUM_TICKET_TAKERS,0),"ticketTakerSaidToProceed", 24);
ticketTakerFullLock = CreateLock("ticketTakerFullLock",19);
ttLobbyLock = CreateMV(GetMV(NUM_TICKET_TAKERS,0)+1,"ttLobbyLock",11);
ttStopLock = CreateLock("ttStopLock",10);
ttStopCondition = CreateCondition("ttStopCondition",15);
	
	AcquireLock(latestIndexLock);
	myIndex = GetMV(latestIndex,0);
	SignalCondition(latestIndexCondition, latestIndexLock);
	ReleaseLock(latestIndexLock);
	

	if(GetMV(PRINT_DEBUG_MESSAGES,0)) FPrint("TicketTaker %d: created\n", 25, 1, myIndex);
	AcquireLock(GetMV(ttStateLock,myIndex));
	SetMV(ttState,myIndex, GetMV(CLERK_AVAILABLE,0));
	
	while(1)
	{
		if(GetMV(ticketTakerFull,myIndex))
		{
			/*/Wait for next movie to start before resuming ticket taking*/
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Waiting for next movie before resuming ticket taking\n", 70, 1, myIndex);
			AcquireLock(GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: waiting on beginTakingTicketsLock\n", 51, 1, myIndex);
			ttWaitBeginTakingTickets: WaitCondition(GetMV(beginTakingTicketsCondition,myIndex), GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalled out of beginTakingTicketsCondition\n", 62, 1, myIndex);
			
			/*/ticketTakerFullLock -> Acquire();*/
			SetMV(ticketTakerFull,myIndex,0);
			SetMV(ttForcedToStop,myIndex,0);
			SetMV(ttState,myIndex,GetMV(CLERK_AVAILABLE,0));
			/*/ticketTakerFullLock -> Release();*/
			
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Acquiring ttInteractionLock[%d]\n", 49, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttInteractionLock,myIndex));
			
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling beginTakingTicketsCondition %d\n", 59, 2, myIndex*100 + myIndex);
			SignalCondition(GetMV(beginTakingTicketsCondition,myIndex), GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: releasing beginTakingTicketsLock\n", 50, 1, myIndex);
			ReleaseLock(GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Ready to resume ticket taking\n", 47, 1, myIndex);
			/*/GetMV(ttInteractionCondition,myIndex) -> Wait(GetMV(ttInteractionLock,myIndex));*/
			/*/GetMV(ttInteractionLock,myIndex) -> Release();*/
		}
		
		/*/Wait for the Customer's tickets*/
		ttInteractionWaitLabel:
		if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: waiting for next Customer\n", 43, 1, myIndex);
		WaitCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
		
		if(GetMV(ttState,myIndex) == GetMV(CLERK_ON_BREAK,0))
		{
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: going on break...\n", 35, 1, myIndex);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttBreakLock %d\n", 42, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttBreakLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling ttInteractionLock %d\n", 49, 2, myIndex*100 + myIndex);
			SignalCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: releasing ttInteractionLock %d\n", 48, 2, myIndex*100 + myIndex);
			ReleaseLock(GetMV(ttInteractionLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: waiting ttBreakLock %d\n", 40, 2, myIndex*100 + myIndex);
			WaitCondition(GetMV(ttBreakCondition,myIndex), GetMV(ttBreakLock,myIndex));
			goto ttBreakLabel;
		}
		
		/*/At this point, check to see if you were woken up by a customer, or by a manager*/
		if(GetMV(ttForcedToStop,myIndex))
		{
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Woken up by Manager for Stop\n", 46, 1, myIndex);
			
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring beginTakingTicketLock %d\n", 52, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling ttInteractionLock %d\n", 49, 2, myIndex*100 + myIndex);
			SignalCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Releasing ttInteractionLock[%d]\n", 49, 2, myIndex*100 + myIndex);
			ReleaseLock(GetMV(ttInteractionLock,myIndex));
			goto ttWaitBeginTakingTickets;
		}
		else
		
		{
			/*/After getting signalled by Customer with tickets		*/
			SetMV(ttState,myIndex,GetMV(CLERK_BUSY,0));
			
			/*/Check if there is room in screening room*/
			AcquireLock(ticketCountBoxLock);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: ticketCountBox currently has %d tickets\n", 57, 2, myIndex*100 + GetMV(ticketCountBox,0));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: processing %d incoming tickets from Customer\n", 62, 2, myIndex*100 + GetMV(numTicketsGiven,myIndex));
			if(GetMV(PRINT_OUTPUT_MESSAGES,0))FPrint("TicketClerk %d has received %d tickets.\n", 41, 2, myIndex*100 + GetMV(numTicketsGiven,myIndex));
			
			AcquireLock(ticketTakerFullLock);
			
			if((GetMV(ticketTakerFull,myIndex)) || (GetMV(ticketCountBox,0) + GetMV(numTicketsGiven,myIndex) > GetMV(SCREENING_ROOM_CAPACITY,0)))
			{
					
				/*/Lock the lines so that no Customer tries to join while TicketTaker gets full*/
				/*/ttLineManager -> Acquire();*/
				/*/If the Customer's group is too large, reject him from seating*/
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Not enough room for Customer!\n", 47, 1, myIndex);
				if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketTaker %d is not allowing the group into theater.  The number of taken tickets is %d and the group size is %d.\n", 117, 3,
					myIndex*10000 + GetMV(ticketCountBox,0)*100 + GetMV(numTicketsGiven,myIndex));
					
				SetMV(ticketTakerFull,myIndex,1);
				SetMV(ticketTakerSaidToProceed,myIndex,0);
				
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: set myself to full, releasing ticketTakerFullLock\n", 67, 1, myIndex);
				if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("Ticketaker %d has stopped taking tickets.\n", 43, 1, myIndex);
				
				/*/ttLineManager -> Release();*/
				ReleaseLock(ticketTakerFullLock);
				ReleaseLock(ticketCountBoxLock);
				
				/*/Signal current Customer with response*/
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Signalling current Customer to go to lobby\n", 60, 1, myIndex);
				SignalCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Releasing ttInteractionLock[%d]\n", 49, 2, myIndex*100 + myIndex);
				ReleaseLock(GetMV(ttInteractionLock,myIndex));
				/*/Wait for Customer signal*/
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Waiting for Customer to go to lobby\n", 53, 1, myIndex);
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Acquiring ttLobbyLock %d\n", 42, 2, myIndex*100 + myIndex);
				AcquireLock(GetMV(ttLobbyLock,myIndex));
				
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Customer went to lobby suttesfully\n", 52, 1, myIndex);
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Releasing ttLobbyLock %d\n", 42, 2, myIndex*100 + myIndex);
				ReleaseLock(GetMV(ttLobbyLock,myIndex));
			}
			else
			{
				ReleaseLock(ticketTakerFullLock);
				
				/*/If the Customer's group can fit, attept him for seating and add tickets to total*/
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: There is room for Customer, adding %d tickets to box.\n", 71, 2, myIndex*100 + GetMV(numTicketsGiven,myIndex));
				if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketTaker %d is allowing the group into the theater.  The number of tickets taken is %d.\n", 92, 2,
					myIndex*100 + GetMV(numTicketsGiven,myIndex));
					
				SetMV(ticketTakerSaidToProceed,myIndex,1);
				SetMV(ticketCountBox, 0, GetMV(ticketCountBox,0) + GetMV(numTicketsGiven,myIndex));	
				
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: New ticket box total: %d\n", 42, 2, myIndex*100 + GetMV(ticketCountBox,0));
				ReleaseLock(ticketCountBoxLock);
				
				/*/Signal current Customer with response*/
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Signalling current Customer to go to screening room\n",69, 1, myIndex);
				SignalCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
				
				/*/Wait for Customer response */
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Waiting for Customer to go to screening room\n", 62, 1, myIndex);
				WaitCondition(GetMV(ttInteractionCondition,myIndex), GetMV(ttInteractionLock,myIndex));
				
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Customer went to screening room suttesfully\n", 61, 1, myIndex);
			}
		}
		
		/*/Release state for manager to edit*/
		if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Releasing ttStateLock\n", 39, 1, myIndex);
		ReleaseLock(GetMV(ttStateLock,myIndex));
		
		/*/Finished dealing with Customer*/
		if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Acquiring ttStateLock\n", 39, 1, myIndex);
		AcquireLock(GetMV(ttStateLock,myIndex));
		
		if(GetMV(ttForcedToStop,myIndex))
		{
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Forced to Stop\n", 32, 1, myIndex);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: stop acq\n", 26, 1, myIndex);
			AcquireLock(ttStopLock);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: b acq\n", 23, 1, myIndex);
			AcquireLock(GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: stop sig\n", 26, 1, myIndex);
			SignalCondition(ttStopCondition, ttStopLock);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: stop wait\n", 27, 1, myIndex);
			WaitCondition(ttStopCondition, ttStopLock);
			ReleaseLock(ttStopLock);
			
			goto ttWaitBeginTakingTickets;
			/*
			if(PRINT_DEBUG_MESSAGES) printf("TicketTaker %d: b wait\n", myIndex);
			GetMV(beginTakingTicketsCondition,myIndex) -> Wait(GetMV(beginTakingTicketsLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) printf("TicketTaker %d: signalled out of beginTakingTicketsCondition\n", myIndex);
				
			//ticketTakerFullLock -> Acquire();
			ticketTakerFull[myIndex] = 0;
			//ticketTakerFullLock -> Release();
			
			GetMV(ttInteractionLock,myIndex) -> Acquire();
			
			if(PRINT_DEBUG_MESSAGES) printf("TicketTaker %d: signalling beginTakingTicketsCondition\n", myIndex);
			GetMV(beginTakingTicketsCondition,myIndex) -> Signal(GetMV(beginTakingTicketsLock,myIndex));
			GetMV(beginTakingTicketsLock,myIndex) -> Release();
			if(PRINT_DEBUG_MESSAGES) printf("TicketTaker %d: Ready to resume ticket taking\n", myIndex);
			//GetMV(ttInteractionCondition,myIndex) -> Wait(GetMV(ttInteractionLock,myIndex));
			//GetMV(ttInteractionLock,myIndex) -> Release();
				*/	
		
		}
		
		
		
		if(GetMV(ttState,myIndex) == GetMV(CLERK_ON_BREAK,0))
		{
			if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketTaker %d is going on break.\n", 35, 1, myIndex);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling Manager that I am going on break\n", 61, 1, myIndex);
						
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttBreakStopLock %d\n", 46, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttBreakStopLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttBreakLock %d\n", 42, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttBreakLock,myIndex));
			
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling ttBreakStopLock %d\n", 47, 2, myIndex*100 + myIndex);
			SignalCondition(GetMV(ttBreakStopCondition,myIndex), GetMV(ttBreakStopLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: releasing ttBreakStopLock %d\n", 46, 2, myIndex*100 + myIndex);
			ReleaseLock(GetMV(ttBreakStopLock,myIndex));
			
			ttBreakLabel:
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttStateLock %d\n", 42, 2, myIndex*100 + myIndex);
			ReleaseLock(GetMV(ttStateLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: waiting ttBreakLock %d\n", 40, 2, myIndex*100 + myIndex);
			WaitCondition(GetMV(ttBreakCondition,myIndex), GetMV(ttBreakLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttStateLock %d\n", 42, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttStateLock,myIndex));
			
			if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketTaker %d is coming off break.\n", 37, 1, myIndex);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: returning from break\n", 38, 1, myIndex);
			
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttInteractionLock %d\n", 48, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttInteractionLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: signalling ttBreakLock %d\n", 43, 2, myIndex*100 + myIndex);
			SignalCondition(GetMV(ttBreakCondition,myIndex), GetMV(ttBreakLock,myIndex));
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: releasing ttBreakLock %d\n", 42, 2, myIndex*100 + myIndex);
			ReleaseLock(GetMV(ttBreakLock,myIndex));
			goto ttInteractionWaitLabel;
		}
		
		/*/If we are full, exit loop and go to top*/
		if(!GetMV(ticketTakerFull,myIndex))
		{
			/*/Lock down lines before attepting next Customer*/
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: acquiring ttLineManager\n", 41, 1, myIndex);
			AcquireLock(ttLineManager);
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: Acquiring ttInteractionLock[%d]\n", 49, 2, myIndex*100 + myIndex);
			AcquireLock(GetMV(ttInteractionLock,myIndex));
			
			/*/Check for customers in line*/
			if(GetMV(ttLineLength,myIndex) > 0)
			{
				if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketTaker %d has a line length %d and is signalling a customer.\n", 67, 2, myIndex*100 + GetMV(ttLineLength,myIndex));
			
				/*/Customers are in line, set state to busy and decrement line length*/
				SetMV(ttState,myIndex,GetMV(CLERK_BUSY,0));
				SetMV(ttLineLength,myIndex, GetMV(ttLineLength,myIndex) - 1);
				
				/*/Wake up next customer*/
				SignalCondition(GetMV(ttLineCondition,myIndex), ttLineManager);
				
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: woke up next Customer\n", 39, 1, myIndex);
			}
			else
			{
				/*/No Customers in line, set state to available*/
				SetMV(ttState,myIndex, GetMV(CLERK_AVAILABLE,0));
				if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: no Customers in line\n", 38, 1, myIndex);
				if(GetMV(PRINT_OUTPUT_MESSAGES,0)) FPrint("TicketClerk %d has no one in line.  I am available for a customer.\n", 68, 1, myIndex);
			}
			
			/*/Release and allow other Customers to search lines*/
			if(PRINT_DEBUG_MESSAGES) FPrint("TicketTaker %d: releasing ttLineManager\n", 41, 1, myIndex);
			ReleaseLock(ttLineManager);
		}
	}
}